﻿using System.Windows;
using System.Windows.Controls;

namespace Bread.Mvc.WPF;

public class BindablePasswordBox : Decorator
{
    /// <summary>
    /// The password dependency property.
    /// </summary>
    public static readonly DependencyProperty PasswordProperty;

    private bool isPreventCallback;
    private RoutedEventHandler savedCallback;

    /// <summary>
    /// Static constructor to initialize the dependency properties.
    /// </summary>
    static BindablePasswordBox()
    {
        PasswordProperty = DependencyProperty.Register(
            "Password",
            typeof(string),
            typeof(BindablePasswordBox),
            new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnPasswordPropertyChanged))
        );
    }

    /// <summary>
    /// Saves the password changed callback and sets the child element to the password box.
    /// </summary>
    public BindablePasswordBox()
    {
        savedCallback = HandlePasswordChanged;

        PasswordBox passwordBox = new PasswordBox();
        passwordBox.PasswordChanged += savedCallback;
        Child = passwordBox;
    }

    /// <summary>
    /// The password dependency property.
    /// </summary>
    public string Password
    {
        get { return GetValue(PasswordProperty) as string ?? string.Empty; }
        set { SetValue(PasswordProperty, value); }
    }

    /// <summary>
    /// Handles changes to the password dependency property.
    /// </summary>
    /// <param name="d">the dependency object</param>
    /// <param name="eventArgs">the event args</param>
    private static void OnPasswordPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs eventArgs)
    {
        BindablePasswordBox bindablePasswordBox = (BindablePasswordBox)d;
        PasswordBox passwordBox = (PasswordBox)bindablePasswordBox.Child;

        if (bindablePasswordBox.isPreventCallback) {
            return;
        }

        passwordBox.PasswordChanged -= bindablePasswordBox.savedCallback;
        passwordBox.Password = (eventArgs.NewValue != null) ? eventArgs.NewValue.ToString() : "";
        passwordBox.PasswordChanged += bindablePasswordBox.savedCallback;
    }

    /// <summary>
    /// Handles the password changed event.
    /// </summary>
    /// <param name="sender">the sender</param>
    /// <param name="eventArgs">the event args</param>
    private void HandlePasswordChanged(object sender, RoutedEventArgs eventArgs)
    {
        PasswordBox passwordBox = (PasswordBox)sender;

        isPreventCallback = true;
        Password = passwordBox.Password;
        isPreventCallback = false;
    }
}
